from django.utils import simplejson as json
from django.http import HttpResponseRedirect, HttpResponse
from django.http import HttpResponseBadRequest, HttpResponseServerError

from urllib import urlencode, splitquery
from cgi import parse_qsl
from traceback import format_exc

import re

def _get_request_param(request, name):
    return request.GET[name]

def view_graph(request, path = None):
    try:
        if path == 'oauth/access_token':
            return view_oauth_access_token(request)
        if path == 'oauth/authorize':
            return view_oauth_authorize(request)

        # Otherwise, handle normal graph object accesses.
        return handle_object(request, path)

    except:
        return HttpResponseServerError(format_exc())

_authorization_codes = {}

_access_tokens = {}
_access_tokens_reverse = {}

from random import choice
from string import uppercase, lowercase, digits

_auth_code_alphabet = uppercase + (digits * 5)
_access_token_alphabet = uppercase + digits

NR_AUTH_CODE = 5
NR_ACCESS_TOKEN = 10

def generateCode(alphabet, nr):
    return ''.join(choice(alphabet) for x in xrange(nr))

def generateAuthCode():
    while True:
        code = generateCode(_auth_code_alphabet, NR_AUTH_CODE)
        if code not in _authorization_codes:
            return code

        # XXX program a break

def generateAccessToken():
    return generateCode(_access_token_alphabet, NR_ACCESS_TOKEN)

def checkAccess(user_id, access_token):
    return True # since runtime doesn't persist over dev server reloads
    return _access_tokens.get(user_id) == access_token

def view_oauth_authorize(request):
    # client_id
    ##    for (name, value) in request.GET.iteritems():
    ##        print '    %s: %r' % (name, str(value)[:100])

    user_id = _get_fb_user_cookie(request)
    if user_id is None:
        # Perform login.
        args = dict()
        redirect_uri = request.GET.get('redirect_uri')
        if redirect_uri is not None:
            args['redirect_uri'] = redirect_uri

        url = rebuild_query('/facebook/login', args)
        return HttpResponseRedirect(url)

    # Perform authorization.
    code = generateAuthCode()
    _authorization_codes[code] = user_id

    access_token = generateAccessToken()
    _access_tokens[user_id] = access_token
    _access_tokens_reverse[access_token] = user_id

    redirect_uri = _get_request_param(request, 'redirect_uri')
    url = rebuild_query(redirect_uri, dict(code = code))
    return HttpResponseRedirect(url)

def view_oauth_access_token(request):
    # client_id
    # client_secret
    # redirect_uri

    code = _get_request_param(request, 'code')
    user_id = _authorization_codes.get(code)
    if user_id is not None:
        access_token = _access_tokens.get(user_id)
        del _authorization_codes[code]

        response = dict(access_token = access_token)

        # Ugh, which is it?
        response = urlencode(response)
        # response = json.dumps(response)

        return HttpResponse(response)

# r'(?:([^/]+)/+)*([^/]+)?'
# r'/*(?:([^/]+)/*)*'
def _get_path_parts(path):
    return path.split('/')

def handle_object(request, path):
    parts = _get_path_parts(path)

    data = dict()
    if len(parts):
        data['name'] = parts[0]

    if len(parts) > 1:
        if parts[1] == 'feed':
            access_token = request.REQUEST.get('access_token')

            user_id = parts[0]
            try: user_id = int(user_id)
            except ValueError: pass

            if not checkAccess(user_id, access_token):
                response = dict(message = 'Invalid access: %r' % access_token)
                return HttpResponseBadRequest(json.dumps(response, indent = 1))

            doUserPostMessage(user_id, request.REQUEST.get('message'))
    else:
        access_token = request.GET.get('access_token')
        user_id = _access_tokens_reverse.get(access_token)

        data['link'] = '/facebook/%s' % parts[0] # or user_id
        data['id'] = user_id

    return HttpResponse(json.dumps(data, indent = 1))

def doUserPostMessage(user_id, message):
    print '[User #%s:Post] %s' % (user_id, message)

from django import template
LOGIN_SCREEN = '''
{% if have_user %}
  <h2>User #{{ fb_user }} Logged In!</h2>
{% else %}
  <h2>Log In:</h2>
  <form action="/facebook/login" method="post">
    <input type="input" name="fb_sig_user" value="{{ fb_sig_user }}" />
    <input type="hidden" name="redirect_uri" value="{{ redirect_uri }}" />
    <input type="submit" />
  </form>
{% endif %}

<p>fb_sig_user: {{ fb_sig_user }}</p>

<table>
{% for cookie in cookies %}
<tr><td>{{ cookie.0|escape }}</td><td style="border: solid thin;">{{ cookie.1|escape }}</td></tr>
{% endfor %}
</table>

<form action="/facebook/logout" method="get">
  <input type="submit" value="Logout" />
</form>
'''

FB_USER_ID_VAR = 'facade_fb_user'

def view_login(request):
    # Allow a initial login.
    redirect_uri = request.REQUEST.get('redirect_uri')

    # Generate appropriate response type (page or redirect) based on method.
    fb_user = None
    if request.method == 'POST':
        if redirect_uri is not None:
            response = HttpResponseRedirect(redirect_uri)
        else:
            response = HttpResponse()

        fb_sig_user = request.POST.get('fb_sig_user')
        if fb_sig_user is not None:
            fb_sig_user = fb_sig_user.strip()

        if not fb_sig_user:
            fb_sig_user = request.GET.get('fb_sig_user')

        if fb_sig_user is not None:
            # Set cookie -- Logged In:
            fb_sig_user = int(fb_sig_user)
            fb_user = fb_sig_user

            _set_fb_user_cookie(response, fb_user)
    else:
        fb_sig_user = request.GET.get('fb_sig_user')
        response = HttpResponse()

    # Draw login/profile screen.
    if fb_user is None:
        fb_user = _get_fb_user_cookie(request)

    # Process response (building page content if not redirect).
    if request.method == 'GET':
        t = template.Template(LOGIN_SCREEN)
        cx = template.Context(dict(fb_user = fb_user,
                                   have_user = bool(fb_user is not None),
                                   fb_sig_user = fb_sig_user or '',
                                   redirect_uri = redirect_uri,
                                   cookies = _get_cookies(request.COOKIES)))

        response.content = t.render(cx)

    return response

def view_logout(request):
    # What about redirect?
    redirect_uri = request.REQUEST.get('redirect_uri') # GET, really
    if redirect_uri is not None:
        response = HttpResponseRedirect(redirect_uri)
    else:
        response = HttpResponse()

    _delete_fb_user_cookie(response)
    return response

def _set_fb_user_cookie(response, user_id):
    response.set_cookie(FB_USER_ID_VAR, value = str(user_id))
def _get_fb_user_cookie(request):
    return request.COOKIES.get(FB_USER_ID_VAR)
def _delete_fb_user_cookie(response):
    response.delete_cookie(FB_USER_ID_VAR)

def _get_cookies(cookies):
    # Will not include newly set cookies (on response object)
    return cookies.iteritems()

def default_view(request):
    return HttpResponse(str(request.COOKIES))

# Utilities.
def simple_parse_qs(qs):
    # Evaluate only last value for each key-pair parameter.
    r = dict()
    for (name, value) in parse_qsl(qs):
        r[name] = value

    return r

def rebuild_query(original, args):
    # Reduce original query string to dictionary, update with existing
    # dictionary, then rebuild new path with query string.

    (path, query) = splitquery(original)
    if query is None:
        query = args
    else:
        query = simple_parse_qs(query)
        query.update(args)

    return '%s?%s' % (path, urlencode(query))
